using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AutoTest.Core.Utils;
using Castle.MicroKernel.Registration;
using Castle.Windsor;

namespace AutoTest.Core.Infrastructure.IoC
{
    /// <summary>
    /// See here for more
    /// http://blog.theagileworkshop.com/2009/06/09/extension-methods-we-use-for-auto-registration-in-castle-windsor/
    /// </summary>
    public static class WindsorExtension
    {
        public static BasedOnDescriptor FirstInterfaceOnType(this ServiceDescriptor serviceDescriptor)
        {
            return serviceDescriptor.Select((type, baseType) =>
                                                {
                                                    var interfaces =
                                                        type.GetInterfaces().Except(type.BaseType.GetInterfaces());
                                                    if (interfaces.Count() == 0)
                                                        return null;
                                                    return new[] {interfaces.First()};
                                                });
        }

        public static BasedOnDescriptor FromGenericInterfaces(this ServiceDescriptor serviceDescriptor, IEnumerable<Type> genericInterfaces)
        {
            return serviceDescriptor.Select((type, baseType) =>
                                                {
                                                    var interfaces = (type).FindGenericInterfaces(genericInterfaces);
                                                    if (interfaces == null || interfaces.Count() == 0)
                                                        return null;
                                                    return interfaces.ToArray();
                                                });
        }

        public static BasedOnDescriptor InterfaceMatchingClassName(this ServiceDescriptor serviceDescriptor)
        {
            return serviceDescriptor.Select((type, baseType) =>
                                                {
                                                    var interfaces =
                                                        type.GetInterfaces().Where(i => i.Name == "I" + type.Name);
                                                    if (interfaces.Count() == 0)
                                                        return null;
                                                    return new[] {interfaces.First()};
                                                });
        }

        public static void RegisterAllTypesWithInterfaceMatchingClassNameExcept(this IWindsorContainer container,
                                                                                params Type[] typesToIgnore)
        {
            container.Register(AllTypes
                                   .FromAssembly(Assembly.GetCallingAssembly())
                                   .Where(HasInterfaceThatMatchesTheClassName)
                                   .Unless(type => IsATypeToIgnore(type, typesToIgnore))
                                   .Configure(component => component.LifeStyle.Singleton)
                                   .WithService.InterfaceMatchingClassName());
        }

        public static void RegisterAllTypesImplementing(this IWindsorContainer container, params Type[] interfaces)
        {
            container.Register(AllTypes
                                   .FromAssembly(Assembly.GetCallingAssembly())
                                   .Where(type => interfaces.Any(i => type.ImplementsInterface(i)))
                                   .Configure(component => component.LifeStyle.Singleton)
                                   .WithService.FromGenericInterfaces(interfaces.ToList()));
        }

        private static bool HasInterfaceThatMatchesTheClassName(Type type)
        {
            return type.GetInterfaces().Any(i => i.Name == "I" + type.Name);
        }

        private static bool IsATypeToIgnore(Type type, IEnumerable<Type> typesToIgnore)
        {
            return typesToIgnore.Any(typeToIgnore => typeToIgnore == type);
        }
    }
}